﻿using System;
using System.Collections.Generic;
using System.Linq;
using Best.SmartCms.Config;
using Best.SmartCms.Core.Utility;
using Best.SmartCms.Utility;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace Best.SmartCms.Common
{
    /// <summary>
    /// 
    /// </summary>
    public class SlowSQLLoggerProvider : ILoggerProvider
    {
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="categoryName"></param>
        /// <returns></returns>
        public ILogger CreateLogger(string categoryName)
        {
            if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command")
                return new EFSlowSQLLogger(categoryName);

            return NullLogger.Instance;
        }

        /// <summary>
        /// 
        /// </summary>
        private class EFSlowSQLLogger : ILogger
        {
            /// <summary>
            /// 
            /// </summary>
            public string CategoryName { get; set; }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="categoryName"></param>
            public EFSlowSQLLogger(string categoryName)
            {
                CategoryName = categoryName;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="logLevel"></param>
            /// <returns></returns>
            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <typeparam name="TState"></typeparam>
            /// <param name="state"></param>
            /// <returns></returns>
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <typeparam name="TState"></typeparam>
            /// <param name="logLevel"></param>
            /// <param name="eventId"></param>
            /// <param name="state"></param>
            /// <param name="exception"></param>
            /// <param name="formatter"></param>
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                try
                {
                    var dbCommandState = state as IEnumerable<KeyValuePair<string, object>>;
                    var elapsedObj = dbCommandState?.FirstOrDefault(t => t.Key.Equals("elapsed", StringComparison.OrdinalIgnoreCase)).Value;
                    int elapsed = 0;
                    if (elapsedObj != null)
                        int.TryParse(elapsedObj?.ToString(), out elapsed);

                    if (!int.TryParse(Configs.EfCoreSlowSqlWarn, out var slowSqlThresholdWarn))
                        slowSqlThresholdWarn = 10;

                    if (!int.TryParse(Configs.EfCoreSlowSqlError, out var slowSqlThresholdError))
                        slowSqlThresholdError = 500;

                    if (elapsed > slowSqlThresholdError)
                    {
                        NLogManager.LogError($"EFCoreSlowSQL: >{slowSqlThresholdError}, detail:{formatter?.Invoke(state, exception)}");
                    }
                    else if (elapsed > slowSqlThresholdWarn)
                    {
                        NLogManager.LogWarn($"EFCoreSlowSQL: >{slowSqlThresholdWarn}, detail:{formatter?.Invoke(state, exception)}");
                    }
                }
                catch (Exception ex)
                {
                    NLogManager.LogError(ex);
                }
            }
        }
    }
}
